/*
 * Automato.cpp
 *
 *  Created on: 04/10/2012
 *      Author: vagner
 */

#include "Automato.h"
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cstdio>

Automato::Automato() {
	// Do nothing

}

Automato::Automato(const char* arquivoEntrada) {

	std::string linha;
	std::vector < std::string > tokens;
	std::vector<std::string>::const_iterator itTokens;
	int numEstados, idEstadoInicial, idEstadoFinal;
	int estadoOrigem, estadoDestino;
	std::string simbolo;
	Item aux;

	abreArquivoEntrada(arquivoEntrada);

	//Lendo a quantidade de estados do automato
	linha = leiaProxLinha();
	splitEmVetor(linha, tokens);
	numEstados = atoi(tokens.at(0).c_str());
	//std::cout << "Quantidade de estados: " << numEstados << std::endl;
	//Criando os estados
	this->numEstados = numEstados;
	for (int i = 0; i < numEstados + 1; i++) {

		this->E.push_back(Estado(i));
	}
	tokens.clear();

	//Lendo o alfabeto
	linha = leiaProxLinha();
	splitEmVetor(linha, tokens);
	//std::cout << "Alfabeto:" << std::endl;
	for (itTokens = tokens.begin(); itTokens != tokens.end(); itTokens++) {
		//std::cout << (*itTokens) << " ";
		simbolo = *itTokens;
		insereSimbolo(simbolo[0]);
	}
	std::cout << std::endl;
	tokens.clear();

	this->tabelaTransicao = new MyMatrix((this->numEstados + 1),
			this->sigma.size());
	aux.valor = this->numEstados;
	tabelaTransicao->defineValorPadrao(aux);

	//Lendo o estado inicial
	linha = leiaProxLinha();
	splitEmVetor(linha, tokens);
	idEstadoInicial = atoi(tokens.at(0).c_str());
	//std::cout << "Estado inicial: " << idEstadoInicial << std::endl;
	setEstadoInicial(idEstadoInicial);
	tokens.clear();

	//Lendo estado finais
	linha = leiaProxLinha();
	splitEmVetor(linha, tokens);
	//std::cout << "Estado(s) Final(is) " << std::endl;
	for (itTokens = tokens.begin(); itTokens != tokens.end(); itTokens++) {
		//std::cout << (*itTokens) << " ";
		idEstadoFinal = atoi((*itTokens).c_str());
		setEstadoFinal(idEstadoFinal);
	}
	std::cout << std::endl;
	tokens.clear();

	while ((linha = leiaProxLinha()) != "EOF") {
		splitEmVetor(linha, tokens);

		estadoOrigem = atoi(tokens.at(0).c_str());
		simbolo = tokens.at(1);
		estadoDestino = atoi(tokens.at(2).c_str());

		//std::cout << "Estado Origem: " << estadoOrigem << std::endl;
		//std::cout << "Simbolo " << simbolo << std::endl;
		//std::cout << "Estado Destino: " << estadoDestino << std::endl;
		insereTabelaTransicao(estadoOrigem, simbolo[0], estadoDestino);
		tokens.clear();
	}
	fechaArquivoEntrada();
}

void Automato::insereSimbolo(char& s) {

	this->sigma.push_back(s);
}

void Automato::insereTabelaTransicao(int& estadoOrigem, char& simbolo,
		int& estadoDestino) {
	Item aux;
	int indexSimbolo = getIndexSimbolo(simbolo);
	aux.valor = estadoDestino;

	if (this->tabelaTransicao == 0) {
		this->tabelaTransicao = new MyMatrix(this->E.size(),
				this->sigma.size());
	} else {
		tabelaTransicao->insereItem(aux, estadoOrigem, indexSimbolo);
	}
}

int Automato::getIndexSimbolo(char& simbolo) const {
	int tamAlfabeto = sigma.size();
	Alfabeto::iterator it;
	for (int i = 0; i < tamAlfabeto; i++) {
		if (sigma.at(i) == simbolo) {
			return i;
		}
	}
	return -1;
}

int Automato::funcaoTransicao(int& idEstado, char simbolo) const {

	int indexSimbol = getIndexSimbolo(simbolo);
	Item itemAux;
	itemAux.valor = -1;

	if (indexSimbol == -1) {
		std::cout << "Simbolo informado " << simbolo
				<< " não pertence ao alfabeto! O programa será encerrado!"
				<< std::endl;
		_exit(-1);
	} else if (idEstado > numEstados) {
		std::cout << "Estado informado " << idEstado
				<< " é invalido. O programa será encerrado" << std::endl;
	} else {

		tabelaTransicao->getItem(idEstado, indexSimbol, itemAux);
	}

	return (itemAux.valor);
}

void Automato::getEstadosFinais(std::vector<int>& ListaEstadoFinais) const {

	std::vector<Estado>::const_iterator itEstados;

	for (itEstados = E.begin(); itEstados != E.end(); itEstados++) {
		if (itEstados->isEstadoFinal()) {
			int idEstadoFinal = itEstados->getID();
			ListaEstadoFinais.push_back(idEstadoFinal);
		}
	}
}

void Automato::setEstadoFinal(int& idEstado) {
	if (idEstado > numEstados) {
		std::cout << "Estado informado " << idEstado
				<< " é invalido. O programa será encerrado" << std::endl;
	} else {
		E.at(idEstado).setEstadoFinal();

	}
}

int Automato::getEstadoInicial() const {

	std::vector<Estado>::const_iterator itEstados;
	int idEstadoIncial = -1; //Estado de erro caso não haja nenhum estado definido como inicial

	for (itEstados = E.begin(); itEstados != E.end(); ++itEstados) {
		if (itEstados->isEstadoInicial()) {
			idEstadoIncial = itEstados->getID();
		}
	}
	return idEstadoIncial;
}

void Automato::setEstadoInicial(int & idEstado) {
	if (idEstado > numEstados) {
		std::cout << "Estado informado " << idEstado
				<< " é invalido. O programa será encerrado" << std::endl;
	} else if (!isEstadoInicalDefinido) {
		E.at(idEstado).setEstadoInicial();
		isEstadoInicalDefinido = true;
	} else {
		std::cout << "Ja existe um estado definido como final! Nada será feito."
				<< std::endl;
	}
}

bool Automato::reconhecePalavra(std::string& w) {
	bool palavraAceita = false;
	int tamPalavra = (int) w.size();
	int idEstadoInicial = this->getEstadoInicial();
	int proxEstado;

	proxEstado = idEstadoInicial;

	if (idEstadoInicial != -1) {
		for (int i = 0; i < tamPalavra; i++) {
			proxEstado = funcaoTransicao(proxEstado, w[i]);
		}

		if (this->isEstadoFinal(proxEstado)) {
			palavraAceita = true;
		}
	} else {
		std::cout
				<< "Nao ha definicao de estado inicial. Palavra nao pode ser reconhecida"
				<< std::endl;
	}
	return palavraAceita;
}

bool Automato::isEstadoFinal(int & idEstado) const {

	bool isFinal = false;
	std::vector<Estado>::const_iterator itEStado;

	if (idEstado > numEstados) {
		std::cout << "Estado informado " << idEstado
				<< " é invalido. O programa será encerrado" << std::endl;
	} else {
		isFinal = E.at(idEstado).isEstadoFinal();
	}

	return isFinal;
}

bool Automato::isEstadoInicial(int& idEstado) const {

	bool isInicial = false;

	if (idEstado > numEstados) {
		std::cout << "Estado informado " << idEstado
				<< " é invalido. O programa será encerrado" << std::endl;
	} else {
		isInicial = E.at(idEstado).isEstadoInicial();
	}

	return isInicial;

}

void Automato::show() const {

	std::vector<Estado>::const_iterator itEstado;
	std::vector<char>::const_iterator itAlfabeto;
	std::vector<int>::const_iterator itInteiro;
	std::vector<int> estadosFinais;
	int idEstado;
	std::cout << "M = ({";
	for (itEstado = E.begin(); itEstado != E.end(); itEstado++) {

		idEstado = itEstado->getID();

		if (itEstado == E.begin()) {
			std::cout << idEstado;
		} else {
				std::cout << "," << itEstado->getID();
		}

	}
	std::cout << "},{";
	for (itAlfabeto = sigma.begin(); itAlfabeto != sigma.end(); itAlfabeto++) {
		if (itAlfabeto == sigma.begin()) {
			std::cout << (*itAlfabeto);
		} else {
			std::cout << "," << (*itAlfabeto);
		}

	}
	std::cout << "},";
	std::cout << "delta(vide tabela)," << this->getEstadoInicial() << ",{";
	this->getEstadosFinais(estadosFinais);
	for (itInteiro = estadosFinais.begin(); itInteiro != estadosFinais.end();
			itInteiro++) {
		if (itInteiro == estadosFinais.begin()) {
			std::cout << (*itInteiro);
		} else {
			std::cout << "," << (*itInteiro);
		}

	}
	std::cout << "});" << std::endl;
	std::cout << "Tabela de Transições(Delta)" << std::endl;
	std::cout << "* Estado (n+1), onde n é o numero de estados,"
			" é  estado de erro do automato" << std::endl;
	this->tabelaTransicao->show();

}

int Automato::getNumEstados()const{
	return this->numEstados;
}

char Automato::getSimbolo(int& i){
	return this->sigma.at(i);
}

std::vector<char>& Automato::getAlfabeto(){
	return this->sigma;
}

Automato::~Automato() {
	delete tabelaTransicao;
}
